﻿using System.Linq.Expressions;
using System.Runtime.InteropServices;
using System.Windows.Media;

namespace JobDemo02
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");

            ImagingContext ctx = new ImagingContext(new JobContext(),new ImagingContextOptions());

            Task.Run(() =>
            {
                int a = 0;
                while (true)
                {
                    ctx.Write(new ImagingResult() { Index = a + 1, Kind = PatternKind.High, IsManual = false });
                    Console.WriteLine("Working...");
                    a++;
                    Thread.Sleep(1000);
                }
            });

            _ = Run(ctx, RunPostProcessing);

            Console.ReadKey();
        }

        async static Task Run(ImagingContext ctx,
        Func<IAsyncEnumerable<ImagingResult>, Task> runProcessing)
        {
            try
            {
                var jobContext = ctx.JobContext;
                var pipeline = new ProcessingPipeline(jobContext.MessageQueue);
                var source = pipeline.Process(ctx.Source);
                var processing = runProcessing(ctx.Source);
            }
            catch (Exception ex)
            {
                await ctx.Complete(ex).ConfigureAwait(false);
                ctx.JobContext.Complete(ex);
                throw;
            }
        }



        async static Task RunPostProcessing(IAsyncEnumerable<ImagingResult> source)
        {
            await foreach (var result in source.ConfigureAwait(false))
            {
                Console.WriteLine($"RunPostProcessing {result.Index}");
            }
            
        }
    }





    public enum PatternKind
    {
        High,
        Align,
        Mp,
        Addr,
        PreDose,
        Focus,
    }

    public record class ImagingResult
    {
        public int Index { get; init; }

        public PatternKind Kind { get; init; }

        public bool IsManual { get; init; }

        public ImageData ImageData { get; init; }

        public double MatchDx { get; init; }

        public double MatchDy { get; init; }

        public int Score { get; init; }

        public int Threshold { get; init; }

        public object Payload { get; init; }

        public ImagingResult Addressing { get; init; }

        public bool ThresholdReached => IsManual || Score >= Threshold;
    }

    public record class AlignmentHint(double StageX, double StageY, ImageData Image);

    public record class ImageQuality(double Quality, double Current);


    public record ImageData(int Width, int Height, byte[] Data, PixelFormat Format);




    public class PatternResult
    {
        public int Index => Image.Index;

        public bool RequireManualAssist { get; set; }

        public ImagingResult Image { get; set; }

        public (ImageData, double)[] Values { get; set; } = [];
    }

    class ProcessingPipeline(MessageQueue<PatternResult> queue)
    {

        public async IAsyncEnumerable<PatternResult> Process(IAsyncEnumerable<ImagingResult> source)
        {
            await foreach (var pImg in source.ConfigureAwait(false))
            {
                switch (pImg.Kind)
                {
                    case PatternKind.Align:
                        DispatchWithMarker(pImg, 500);
                        goto case PatternKind.High;
                    // OM frames already dispatched
                    case PatternKind.High:
                        if (!pImg.ThresholdReached)
                        {
                            queue.Send(new PatternResult
                            {
                                Image = pImg,
                                RequireManualAssist = true
                            });
                            break;
                        }
                        else
                        {
                            var img = new PatternResult
                            {
                                Image = pImg,
                            };
                            queue.Send(img);
                            yield return img;
                        }
                        break;
                    case PatternKind.Addr:
                        DispatchWithMarker(pImg, 500);
                        if (pImg.ThresholdReached)
                        {
                            // addressing images will be sent downstream with mp
                            queue.Send(new PatternResult
                            {
                                Image = pImg,
                            });
                        }
                        else
                        {
                            
                                queue.Send(new PatternResult
                                {
                                    Image = pImg,
                                    RequireManualAssist = true
                                });
                                break;

                            // there will be no mp from upstream if addressing fails
                            yield return new PatternResult
                            {
                                Image = new ImagingResult
                                {
                                    Index = pImg.Index,
                                    Kind = PatternKind.Mp,
                                }
                            };
                        }
                        break;
                    case PatternKind.PreDose:
                    case PatternKind.Focus:
                        Dispatch(pImg, 30);
                        break;
                    case PatternKind.Mp:
                        
                        break;
                }
            }
        }

        void Dispatch(ImageData img, object data, int time)
        {
        }

        void Dispatch(ImageData img, int time)
        {
            Dispatch(img, null, time);
        }

        void Dispatch(ImagingResult pImg, int time)
        {
            Dispatch(pImg.ImageData, pImg.Payload, time);
        }

        void DispatchWithMarker(ImagingResult pImg, int time)
        {
            var img = pImg.ImageData;
            var b = new byte[img.Data.Length];
            Array.Copy(img.Data, b, b.Length);
            var copy = img with { Data = b };
            Dispatch(copy, pImg.Payload, time);
        }
    }
}
